import { FormInstance, message, Upload, UploadFile, Image } from "antd";
import {
	CSSProperties,
	forwardRef,
	ForwardRefRenderFunction,
	HTMLAttributes,
	MutableRefObject,
	useCallback,
	useContext,
	useEffect,
	useImperativeHandle,
	useRef,
	useState,
} from "react";
import { BaseResponse, toast } from "src/util";
import { PlusOutlined, ReloadOutlined } from "@ant-design/icons";
import { RcFile, UploadChangeParam } from "antd/lib/upload";
import styled from "styled-components";
import type { DragEndEvent } from "@dnd-kit/core";
import { DndContext } from "@dnd-kit/core";
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { css } from "@emotion/css";
import ImgPreview from "../dataPresentation/ImgPreview";
import { cloneDeep } from "lodash";
import { AppContext } from "@/providers";

interface ImgUploadProps {
	formRef?: MutableRefObject<FormInstance | null>;
	onChange?: (path: string | string[] | null) => void;
	field?: string;
	action?: string;
	value?: string | string[] | null;
	className?: HTMLAttributes<HTMLDivElement>["className"];
	size?: string | number;
	multiple?: boolean;
	zoom?: number;
}

export interface ImgUploadRef {
	setUrl: (url: string) => void;
	setFileList: (list: UploadFile<BaseResponse<string>>[]) => void;
}

const ImgUpload: ForwardRefRenderFunction<ImgUploadRef, ImgUploadProps> = (
	{ formRef, onChange, field, action, value, className, zoom = 1, multiple = false },
	ref
) => {
	const isMultiple = (value: string | string[]) => Object.prototype.toString.call(value) === "[object Array]";
	useImperativeHandle(ref, () => ({
		setUrl(urlOrList: string | string[]) {
			if (multiple) {
				isMultiple(urlOrList) && setFileList((urlOrList as string[]).map((item) => ({ item, uid: "0", name: "" })));
			} else {
				typeof urlOrList === "string" && setFileList([{ url: urlOrList, uid: "0", name: "" }]);
			}
		},
		setFileList,
	}));
	useEffect(() => {
		if (value) {
			if (multiple && isMultiple(value)) {
				setFileList(
					(value as string[]).map((item, index) => ({ url: `${REACT_APP_BASEURL}${item}`, uid: index.toString(), name: "" }))
				);
			} else {
				setFileList([{ url: `${REACT_APP_BASEURL}${value}`, uid: "0", name: "" }]);
			}
		}
	}, [value]);
	const [fileList, setFileList] = useState<UploadFile<BaseResponse<string>>[]>([]);
	const { REACT_APP_BASEURL } = process.env;
	const imgField = field || "avatar";
	const loadedCount = useRef(0);
	const fileListMap = useRef<UploadFile<BaseResponse<string>>[]>([]);

	const beforeUpload = (file: RcFile) => {
		const isJpgOrPngOrWebp = file.type === "image/jpeg" || file.type === "image/png" || file.type === "image/webp";
		if (!isJpgOrPngOrWebp) {
			message.error("只允许上传JPG/PNG/webp的图片!");
		}
		const isLt8M = file.size / 1024 / 1024 < 8;
		if (!isLt8M) {
			message.error("图片大小必须小于8MB!");
		}
		loadedCount.current = fileList.length;
		return isJpgOrPngOrWebp && isLt8M;
	};
	const uploadChange = useCallback(
		(info: UploadChangeParam<UploadFile<BaseResponse<string>>>) => {
			if (info.file.status === "done") {
				if (info.file.response?.code === 200) {
					loadedCount.current++;
					fileListMap.current.push(info.file);
					if (loadedCount.current === info.fileList.length || !multiple) {
						setTimeout(() => {
							const uploadList = document.getElementsByClassName("ant-upload-list")?.[0];
							if (uploadList) {
								const { scrollWidth } = uploadList;
								uploadList.scrollLeft = scrollWidth + 40;
							}
						}, 200);
						toast.success("上传成功");
						info.fileList.forEach((item) => {
							const findFile = fileListMap.current.find((item1) => item1.uid === item.uid);
							if (findFile) {
								item.url = `${REACT_APP_BASEURL}${findFile.response?.data || ""}`;
							}
						});
						if (multiple) {
							formRef?.current?.setFieldValue(
								imgField,
								info.fileList.map((item) => item.url?.substring(REACT_APP_BASEURL?.length || 0))
							);
							onChange?.(info.fileList.map((item) => item.url?.substring(REACT_APP_BASEURL?.length || 0) as string) || []);
						} else {
							formRef?.current?.setFieldValue(imgField, info.file.response.data);
							onChange?.(info.file.response.data || null);
						}
					}
				} else {
					toast.error(info.file.response?.message || "上传失败");
				}
			}
			if (multiple) {
				setFileList(info.fileList);
			} else {
				if (info.fileList[info.fileList.length - 1]) {
					setFileList([info.fileList[info.fileList.length - 1]]);
				} else {
					setFileList([]);
					onChange?.(null);
					formRef?.current?.setFieldValue(imgField, "");
				}
			}
		},
		[fileList]
	);
	const onDragEnd = useCallback(
		({ active, over }: DragEndEvent) => {
			if (active.id !== over?.id) {
				const activeIndex = fileList.findIndex((i) => i.uid === active.id);
				const overIndex = fileList.findIndex((i) => i.uid === over?.id);
				const list = arrayMove(fileList, activeIndex, overIndex);
				formRef?.current?.setFieldValue(
					imgField,
					list.map((item) => item.url?.substring(REACT_APP_BASEURL?.length || 0))
				);
				setFileList(list);
			}
		},
		[fileList]
	);
	return (
		<DndContext onDragEnd={onDragEnd}>
			<SortableContext items={fileList.map((i) => i.uid)} strategy={verticalListSortingStrategy}>
				<StyledUpload
					multiple={multiple}
					name="file"
					zoom={zoom}
					fileList={fileList}
					withCredentials
					headers={{ Authorization: `Bearer ${JSON.parse(localStorage.Authorization || null)}` }}
					accept=".jpg, .jpeg, .png, .webp"
					listType="picture-card"
					className={`avatar-uploader ${
						multiple
							? css`
									.ant-upload-list {
										white-space: nowrap;
										overflow-x: scroll;
										overflow-y: hidden;
									}
							  `
							: ""
					} ${className}`}
					action={`${REACT_APP_BASEURL}${action || "/admin/upload/image"}`}
					beforeUpload={beforeUpload}
					itemRender={(originNode, file) => (
						<DraggableUploadListItem
							fileList={fileList}
							setFileList={setFileList}
							file={file}
							onDelete={(list) => {
								loadedCount.current--;
								if (multiple) {
									formRef?.current?.setFieldValue(
										imgField,
										list.map((item) => item.url?.substring(REACT_APP_BASEURL?.length || 0))
									);
									onChange?.(list.map((item) => item.url?.substring(REACT_APP_BASEURL?.length || 0) as string) || []);
								} else {
									formRef?.current?.setFieldValue(imgField, list[0]?.url || null);
									onChange?.(list[0]?.url || null);
								}
							}}
						/>
					)}
					onChange={uploadChange}
				>
					<div>
						{fileList.length > 0 && !multiple ? (
							<ReloadOutlined style={{ fontSize: 14 / zoom + "px" }} />
						) : (
							<PlusOutlined style={{ fontSize: 14 / zoom + "px" }} />
						)}
						<div style={{ marginTop: 8, fontSize: 14 / zoom + "px" }}>{fileList.length > 0 && !multiple ? "替换" : "上传"}</div>
					</div>
				</StyledUpload>
			</SortableContext>
		</DndContext>
	);
};

export default forwardRef(ImgUpload);
export const StyledUpload = styled(Upload)<{ zoom: number }>`
	zoom: ${({ zoom }) => zoom};
`;

interface DraggableUploadListItemProps {
	file: UploadFile;
	fileList: UploadFile<BaseResponse<string>>[];
	setFileList: (list: UploadFile<BaseResponse<string>>[]) => void;
	onDelete: (fileList: UploadFile<BaseResponse<string>>[]) => void;
}
const DraggableUploadListItem = ({ file, fileList, setFileList, onDelete }: DraggableUploadListItemProps) => {
	const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
		id: file.uid,
	});

	const style: CSSProperties = {
		transform: CSS.Transform.toString(transform),
		transition,
		cursor: "move",
	};

	// prevent preview event when drag end
	const className = isDragging
		? css`
				a {
					pointer-events: none;
				}
		  `
		: "";
	const currentUid = useRef<string>("");
	const [previewSrc, setPreviewSrc] = useState("");
	const { showContextMenu } = useContext(AppContext);
	return (
		<>
			<div
				onContextMenu={(event) => {
					showContextMenu(
						[
							{
								text: "预览",
								onClick: () => setPreviewSrc(file.url || ""),
							},
							{
								text: "删除",
								onClick: () => {
									const copiedList = cloneDeep(fileList);
									const index = copiedList.findIndex((item) => item.uid === currentUid.current);
									copiedList.splice(index, 1);
									setFileList(copiedList);
									onDelete(copiedList);
								},
							},
						],
						event
					);
					currentUid.current = file.uid;
				}}
				ref={setNodeRef}
				style={style}
				className={`${className} w-full h-full`}
				{...attributes}
				{...listeners}
			>
				<div style={{ border: "1px solid #d9d9d9" }} className="p-2 rounded-md">
					<Image src={file.url} width={84} height={84} style={{ objectFit: "contain" }} preview={false} />
				</div>
			</div>
			<ImgPreview src={previewSrc} onHide={() => setPreviewSrc("")} />
		</>
	);
};
